﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Toolkit.Web.Infastructure;
using Toolkit.Web.ExtensionMethods;
using System.Web;
using Toolkit.Web.Resources;

namespace Toolkit.Web.ViewComponents.DataGrid
{
    public sealed class DataGridFilterHelper<T> where T : class
    {
        public DataGridFilterHelper(IReadOnlyDataGridColumn<T> column)
        {
            if (column == null)
            {
                throw new ArgumentNullException("column");
            }

            ReadOnlyDataGridColumn = column;
        }

        public IReadOnlyDataGridColumn<T> ReadOnlyDataGridColumn
        {
            get;
            private set;
        }

        public IHtmlString DefaultFilterSummary()
        {
            StringBuilder markup = new StringBuilder();

            int numberOfAppliedFilters = ReadOnlyDataGridColumn.Filters.Count(f => f.IsFilterApplied());
            string text = string.Empty;
            string title = string.Empty;
            string className = string.Empty;

            // TODO: localize

            if (numberOfAppliedFilters == 0)
            {
                className = "vc-dg-f-notapplied";
                text = "- none -";
                title = "No Filter Applied";
            }
            else
            {
                className = "vc-dg-f-applied";

                foreach (IReadOnlyDataGridColumnFilter filter in ReadOnlyDataGridColumn.Filters.Where(f => f.IsFilterApplied()))
                {
                    if (!string.IsNullOrEmpty(text))
                    {
                        title += " AND ";
                        text += " & ";
                    }
                    title += GetFilterText(filter);
                    text += GetFilterTextSummary(filter);
                }
            }

            markup.AppendFormat(
                @"<div class=""{0}""><div class=""vc-dg-f-icon"" title=""{1}""><span class=""vc-icon""></span></div><div class=""vc-dg-f-text"" title=""{3}""><span class=""vc-text"">{2}</span></div></div>",
                className,
                HttpUtility.HtmlEncode("Click to Edit Filter"),
                HttpUtility.HtmlEncode(text),
                HttpUtility.HtmlEncode(title)
            );
            
            return HtmlStringExtensions.Create(markup.ToString());
        }

        public IHtmlString DefaultFilter(IReadOnlyDataGridColumnFilter filter, int index)
        {
            StringBuilder markup = new StringBuilder();

            markup.Append(GetOperatorSelectMarkup(filter, index));
            
            // Make #2 in the order so a visible field with be found with css selector :first-child.
            markup.Append(GetFilterKeyMarkup(filter, index));
            markup.Append(GetFieldExpressionMarkup(filter, index));
            markup.Append(GetFieldValueTypeMarkup(filter, index));

            if (filter.Type == DataGridColumnFilterType.Search)
            {
                markup.Append(GetSearchMarkup(filter, index));
            }
            else if (filter.Type == DataGridColumnFilterType.Compare)
            {
                markup.Append(GetCompareMarkup(filter, index));
            }
            else if (filter.Type == DataGridColumnFilterType.SingleSelect)
            {
                markup.Append(GetSingleSelectMarkup(filter, index));
            }
            else if (filter.Type == DataGridColumnFilterType.MultiSelect)
            {
                markup.Append(GetMultiSelectMarkup(filter, index));
            }
            else
            {
                throw new NotSupportedException();
            }

            return HtmlStringExtensions.Create(markup.ToString());
        }

        private string GetFilterText(IReadOnlyDataGridColumnFilter filter)
        {
            string text = string.Empty;

            if (filter.AppliedOperator == DataGridFilterOperator.None)
            {
                text = GetOperatorText(filter.AppliedOperator);
            }
            else if (filter.AppliedOperator == DataGridFilterOperator.IsNotEmptyOrNull || filter.AppliedOperator == DataGridFilterOperator.IsEmptyOrNull)
            {
                text = GetOperatorText(filter.AppliedOperator);
            }
            else if (filter.Type == DataGridColumnFilterType.Search)
            {
                text = string.Format(
                    @"{0} ""{1}""", 
                    GetOperatorText(filter.AppliedOperator), 
                    filter.AppliedValues[0]
                );
            }
            else if (filter.Type == DataGridColumnFilterType.SingleSelect)
            {
                if (filter.AppliedOperator == DataGridFilterOperator.IsContainedIn)
                {
                    text = string.Format(
                        @"{0} ""{1}""",
                        GetOperatorText(DataGridFilterOperator.IsEqualTo),
                        filter.AppliedValues[0]
                    );
                }
                else if (filter.AppliedOperator == DataGridFilterOperator.IsNotContainedIn)
                {
                    text = string.Format(
                        @"{0} ""{1}""",
                        GetOperatorText(DataGridFilterOperator.IsNotEqualTo),
                        filter.AppliedValues[0]
                    );
                }
                else
                {
                    text = string.Format(
                        @"{0} ""{1}""",
                        GetOperatorText(filter.AppliedOperator),
                        filter.AppliedValues[0]
                    );
                }
            }
            else if (filter.Type == DataGridColumnFilterType.MultiSelect)
            {
                text = string.Format(
                    @"{0} ""{1}""",
                    GetOperatorText(filter.AppliedOperator),
                    string.Join(@""", """, filter.AppliedValues)
                );
            }
            else if (filter.Type == DataGridColumnFilterType.Compare)
            {
                text = string.Format(
                    @"{0} {1}",
                    GetOperatorText(filter.AppliedOperator),
                    filter.AppliedValues[0]
                );
            }

            return text;
        }

        private string GetFilterTextSummary(IReadOnlyDataGridColumnFilter filter)
        {
            string text = string.Empty;

            // TODO: localize

            if (filter.AppliedOperator == DataGridFilterOperator.None)
            {
                text = string.Format(GetOperatorTextSummaryFormat(filter.AppliedOperator), "- none -");
            }
            else if (filter.AppliedOperator == DataGridFilterOperator.IsNotEmptyOrNull)
            {
                text = string.Format(GetOperatorTextSummaryFormat(filter.AppliedOperator), "Is Not Empty");
            }
            else if (filter.AppliedOperator == DataGridFilterOperator.IsEmptyOrNull)
            {
                text = string.Format(GetOperatorTextSummaryFormat(filter.AppliedOperator), "Is Empty");
            }
            else if (filter.Type == DataGridColumnFilterType.Search)
            {
                text = string.Format(
                    GetOperatorTextSummaryFormat(filter.AppliedOperator),
                    filter.AppliedValues[0]
                );
            }
            else if (filter.Type == DataGridColumnFilterType.SingleSelect)
            {
                if (filter.AppliedOperator == DataGridFilterOperator.IsContainedIn)
                {
                    text = string.Format(
                        GetOperatorTextSummaryFormat(DataGridFilterOperator.IsEqualTo),
                        filter.AppliedValues[0]
                    );
                }
                else if (filter.AppliedOperator == DataGridFilterOperator.IsNotContainedIn)
                {
                    text = string.Format(
                        GetOperatorTextSummaryFormat(DataGridFilterOperator.IsNotEqualTo),
                        filter.AppliedValues[0]
                    );
                }
                else
                {
                    text = string.Format(
                        GetOperatorTextSummaryFormat(filter.AppliedOperator),
                        filter.AppliedValues[0]
                    );
                }
            }
            else if (filter.Type == DataGridColumnFilterType.MultiSelect)
            {
                text = string.Format(
                    @"({0} selected)",
                    filter.AppliedValues.Count
                );
            }
            else if (filter.Type == DataGridColumnFilterType.Compare)
            {
                text = string.Format(
                    GetOperatorTextSummaryFormat(filter.AppliedOperator),
                    filter.AppliedValues[0]
                );
            }

            return text;
        }

        private string GetOperatorText(DataGridFilterOperator op)
        {
            // TODO: localize

            if (op == DataGridFilterOperator.Contains)
            {
                return "Contains";
            }
            else if (op == DataGridFilterOperator.DoesNotContain)
            {
                return "Does Not Contain";
            }
            else if (op == DataGridFilterOperator.EndsWith)
            {
                return "Ends With";
            }
            else if (op == DataGridFilterOperator.IsContainedIn)
            {
                return "Is Contained In";
            }
            else if (op == DataGridFilterOperator.IsEmptyOrNull)
            {
                return "Is Empty or Null";
            }
            else if (op == DataGridFilterOperator.IsEqualTo)
            {
                return "Is Equal To";
            }
            else if (op == DataGridFilterOperator.IsGreaterThan)
            {
                return "Is Greater Than";
            }
            else if (op == DataGridFilterOperator.IsGreaterThanOrEqualTo)
            {
                return "Is Greater Than or Equal To";
            }
            else if (op == DataGridFilterOperator.IsLessThan)
            {
                return "Is Less Than";
            }
            else if (op == DataGridFilterOperator.IsLessThanOrEqualTo)
            {
                return "Is Less Than or Equal To";
            }
            else if (op == DataGridFilterOperator.IsNotContainedIn)
            {
                return "Is Not Contained In";
            }
            else if (op == DataGridFilterOperator.IsNotEmptyOrNull)
            {
                return "Is Not Empty Or Null";
            }
            else if (op == DataGridFilterOperator.IsNotEqualTo)
            {
                return "Is Not Equal To";
            }
            else if (op == DataGridFilterOperator.None)
            {
                return "None";
            }
            else if (op == DataGridFilterOperator.StartsWith)
            {
                return "Starts With";
            }

            throw new NotSupportedException();
        }

        private string GetOperatorTextSummaryFormat(DataGridFilterOperator op)
        {
            if (op == DataGridFilterOperator.Contains)
            {
                return "..{0}..";
            }
            else if (op == DataGridFilterOperator.DoesNotContain)
            {
                return "!..{0}..";
            }
            else if (op == DataGridFilterOperator.EndsWith)
            {
                return "..{0}";
            }
            else if (op == DataGridFilterOperator.IsContainedIn)
            {
                return "{{0}}";
            }
            else if (op == DataGridFilterOperator.IsEmptyOrNull)
            {
                return "{0}";
            }
            else if (op == DataGridFilterOperator.IsEqualTo)
            {
                return "== {0}";
            }
            else if (op == DataGridFilterOperator.IsGreaterThan)
            {
                return "> {0}";
            }
            else if (op == DataGridFilterOperator.IsGreaterThanOrEqualTo)
            {
                return ">= {0}";
            }
            else if (op == DataGridFilterOperator.IsLessThan)
            {
                return "< {0}";
            }
            else if (op == DataGridFilterOperator.IsLessThanOrEqualTo)
            {
                return "<= {0}";
            }
            else if (op == DataGridFilterOperator.IsNotContainedIn)
            {
                return "- {{0}}";
            }
            else if (op == DataGridFilterOperator.IsNotEmptyOrNull)
            {
                return "{0}";
            }
            else if (op == DataGridFilterOperator.IsNotEqualTo)
            {
                return "!= {0}";
            }
            else if (op == DataGridFilterOperator.None)
            {
                return "{0}";
            }
            else if (op == DataGridFilterOperator.StartsWith)
            {
                return "{0}..";
            }

            throw new NotSupportedException();
        }

        private string GetFilterKeyMarkup(IReadOnlyDataGridColumnFilter filter, int index)
        {
            StringBuilder markup = new StringBuilder();

            markup.AppendFormat(
                @"<input type=""hidden"" name=""Filters[{0}].Key"" value=""{1}"" />",
                index,
                HttpUtility.HtmlAttributeEncode(filter.Key)
            );

            return markup.ToString();
        }

        private string GetFieldExpressionMarkup(IReadOnlyDataGridColumnFilter filter, int index)
        {
            StringBuilder markup = new StringBuilder();

            markup.AppendFormat(
                @"<input type=""hidden"" name=""Filters[{0}].FieldExpression.Encrypted"" value=""{1}"" />",
                index,
                HttpUtility.HtmlAttributeEncode(filter.FieldExpression.Encrypted)
            );

            markup.AppendFormat(
                @"<input type=""hidden"" name=""Filters[{0}].FieldExpression.Encoded"" value=""{1}"" />",
                index,
                HttpUtility.HtmlAttributeEncode(filter.FieldExpression.Encoded)
            );

            return markup.ToString();
        }

        private string GetFieldValueTypeMarkup(IReadOnlyDataGridColumnFilter filter, int index)
        {
            StringBuilder markup = new StringBuilder();

            markup.AppendFormat(
                @"<input type=""hidden"" name=""Filters[{0}].FieldValueType"" value=""{1}"" />",
                index,
                HttpUtility.HtmlAttributeEncode(filter.FieldValueType.FullName)
            );

            return markup.ToString();
        }

        private string GetOperatorSelectMarkup(IReadOnlyDataGridColumnFilter filter, int index)
        {
            StringBuilder markup = new StringBuilder();

            markup.AppendFormat(
                @"<select name=""Filters[{0}].Operator"" style=""{1}"">",
                index,
                filter.AvailableOperators.Count <= 1 ? "display:none;" : string.Empty
            );

            if (filter.AvailableOperators != null)
            {
                foreach (DataGridFilterOperator op in filter.AvailableOperators)
                {
                    markup.AppendFormat(
                        @"<option value=""{0}"" title=""{1}"" {2}>{1}</option>",
                        HttpUtility.HtmlAttributeEncode(op.ToString()),
                        HttpUtility.HtmlEncode(GetOperatorText(op)),
                        (filter.AppliedOperator == op) ? @"selected=""selected""" : string.Empty
                    );
                }
            }

            markup.Append(@"</select>");

            return markup.ToString();
        }

        private string GetSearchMarkup(IReadOnlyDataGridColumnFilter filter, int index)
        {
            StringBuilder markup = new StringBuilder();

            markup.AppendFormat(
                @"<input type=""search"" name=""Filters[{0}].FieldValues[0]"" value=""{1}"" />",
                index,
                (filter.AppliedValues != null && filter.AppliedValues.Count > 0) ? HttpUtility.HtmlAttributeEncode(filter.AppliedValues[0]) : string.Empty
            );

            return markup.ToString();
        }

        private string GetCompareMarkup(IReadOnlyDataGridColumnFilter filter, int index)
        {
            StringBuilder markup = new StringBuilder();

            markup.AppendFormat(
                @"<input type=""text"" name=""Filters[{0}].FieldValues[0]"" value=""{1}"" />",
                index,
                (filter.AppliedValues != null && filter.AppliedValues.Count > 0) ? HttpUtility.HtmlAttributeEncode(filter.AppliedValues[0]) : string.Empty
            );

            return markup.ToString();
        }

        private string GetSingleSelectMarkup(IReadOnlyDataGridColumnFilter filter, int index)
        {
            StringBuilder markup = new StringBuilder();

            markup.AppendFormat(
                @"<select name=""Filters[{0}].FieldValues[0]"">",
                index
            );

            if (filter.AvailableValues != null)
            {
                foreach (KeyValuePair<string, string> keyValue in filter.AvailableValues)
                {
                    markup.AppendFormat(
                        @"<option value=""{0}"" title=""{1}"" {2}>{1}</option>",
                        HttpUtility.HtmlAttributeEncode(keyValue.Key),
                        HttpUtility.HtmlEncode(keyValue.Value),
                        (filter.AppliedValues != null && filter.AppliedValues.Contains(keyValue.Key)) ? @"selected=""selected""" : string.Empty
                    );
                }
            }

            markup.Append(@"</select>");

            return markup.ToString();
        }

        private string GetMultiSelectMarkup(IReadOnlyDataGridColumnFilter filter, int index)
        {
            StringBuilder markup = new StringBuilder();

            markup.AppendFormat(
                @"<select multiple=""multiple"" size=""8"" name=""Filters[{0}].FieldValues"">",
                index
            );

            if (filter.AvailableValues != null)
            {
                foreach (KeyValuePair<string, string> keyValue in filter.AvailableValues)
                {
                    markup.AppendFormat(
                        @"<option value=""{0}"" title=""{1}"" {2}>{1}</option>",
                        HttpUtility.HtmlAttributeEncode(keyValue.Key),
                        HttpUtility.HtmlEncode(keyValue.Value),
                        (filter.AppliedValues != null && filter.AppliedValues.Contains(keyValue.Key)) ? @"selected=""selected""" : string.Empty
                    );
                }
            }

            markup.Append(@"</select>");

            return markup.ToString();
        }
    }
}
